package com.wolfking.converter.service;

import com.alibaba.druid.pool.DruidDataSource;
import com.google.common.collect.Maps;
import com.wolfking.converter.entity.SqlDataSource;
import com.wolfking.jeesite.util.OsUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;

@Slf4j
@Component
@SuppressWarnings("all")
public class SqlDataSourceInitService implements CommandLineRunner, DisposableBean {

    @Autowired
    private SqlConfigInterface sqlConfigInterface;

    /**
     * 数据源的初始化和更新
     */
    @Autowired
    private AbstractRoutingDataSource abstractRoutingDataSource;

    private static final Field targetDataSources;

    static {
        try {
            targetDataSources = AbstractRoutingDataSource.class.getDeclaredField("targetDataSources");
            targetDataSources.setAccessible(true);
        } catch (Exception e) {
            throw new Error(e);
        }
    }

    private final Object lock = new Object();

    private final Map<Integer, SqlDataSource> initDataSourceMap = Maps.newConcurrentMap();

    private final Timer timer = new Timer("sql-datasource-init", true);


    @Override
    public void run(String... args) throws Exception {
        if(OsUtils.isNotWindows()) {
            try {
                initDataSource();
            } catch (Exception e) {
                log.warn("", e);
            }
            timer.schedule(new TimerTask() {
                @Override
                public void run() {
                    try {
                        initDataSource();
                    } catch (Exception e) {
                        log.warn("", e);
                    }
                }
            }, 10000, 10000);
        }
    }


    @Override
    public void destroy() {
        initDataSourceMap.clear();
        timer.cancel();
    }

    private void initDataSource() throws Exception {
        synchronized (lock) {
            List<SqlDataSource> sqlDataSources = sqlConfigInterface.queryDataSource();
            Map<Object, Object> originDataSource = (Map<Object, Object>) targetDataSources.get(abstractRoutingDataSource);
            if (initDataSourceMap.isEmpty()) {
                Map<Object, Object> dataSourceMap = Maps.newHashMap();
                originDataSource.forEach((k, v) -> {
                    log.info("close the data source {}", k);
                    DruidDataSource druidDataSource = (DruidDataSource) v;
                    try {
                        druidDataSource.close();
                    } catch (Exception e) {
                        log.error("close datasource error");
                    }
                });
                originDataSource.clear();
                for (SqlDataSource sqlDataSource : sqlDataSources) {
                    if (sqlDataSource.getIsAble()) {
                        try {
                            DruidDataSource druidDataSource = new DruidDataSource();
                            BeanUtils.copyProperties(sqlDataSource, druidDataSource);
//                            druidDataSource.setSocketTimeout(30000);
//                            druidDataSource.setQueryTimeout(30);
                            druidDataSource.setValidationQueryTimeout(60000);
                            druidDataSource.setKeepAliveBetweenTimeMillis(300001);
                            druidDataSource.setTimeBetweenEvictionRunsMillis(300000);
                            druidDataSource.init();
                            dataSourceMap.put(sqlDataSource.getName(), druidDataSource);
                            initDataSourceMap.put(sqlDataSource.getId(), sqlDataSource);
                        } catch (Exception e) {
                            log.error("数据源初始化异常", e);
                        }
                    } else {
                        log.info("sql data source is not able ,not init {}", sqlDataSource.getName());
                    }
                }
                abstractRoutingDataSource.setTargetDataSources(dataSourceMap);
            } else {
                for (SqlDataSource sqlDataSource : sqlDataSources) {
                    boolean close = false;
                    boolean init = false;
                    if (sqlDataSource.getIsAble()) {
                        if (initDataSourceMap.containsKey(sqlDataSource.getId())) {
                            if (initDataSourceMap.get(sqlDataSource.getId()).getUpdateTime().getTime() != sqlDataSource.getUpdateTime().getTime()) {
                                close = true;
                                init = true;
                            }
                        } else {
                            //初始化
                            init = true;
                        }
                    } else {
                        close = true;
                    }

                    if (close) {
                        Object remove = originDataSource.remove(sqlDataSource.getName());
                        if (Objects.nonNull(remove)) {
                            log.info("close the datasource {}", sqlDataSource.getName());
                            DruidDataSource druidDataSource = (DruidDataSource) remove;
                            druidDataSource.close();
                        }
                        initDataSourceMap.remove(sqlDataSource.getId());
                    }
                    if (init) {
                        log.info("{} the datasource {}", close ? "re init" : "init", sqlDataSource.getName());
                        try {
                            DruidDataSource druidDataSource = new DruidDataSource();
                            BeanUtils.copyProperties(sqlDataSource, druidDataSource);
                            druidDataSource.setValidationQueryTimeout(60000);
                            druidDataSource.setKeepAliveBetweenTimeMillis(300001);
                            druidDataSource.setTimeBetweenEvictionRunsMillis(300000);
                            druidDataSource.init();
                            originDataSource.put(sqlDataSource.getName(), druidDataSource);
                            initDataSourceMap.put(sqlDataSource.getId(), sqlDataSource);
                        } catch (Exception e) {
                            log.error("数据源初始化异常", e);
                        }
                    }
                }
                abstractRoutingDataSource.setTargetDataSources(originDataSource);
            }
            abstractRoutingDataSource.afterPropertiesSet();
        }
    }
}
